package data.complex;

import java.util.List;

import data.base.Vector3;



public class Ray extends Vector3{
  private Vector3 direction;
  private double length = 0;
  private Intersection intersection = new Intersection(this);
  
  private static final int QUAD_LEFT = 0;
  private static final int QUAD_RIGHT = 1;
  private static final int QUAD_MIDDLE = 2;
    
  public Ray(Vector3 origin) {
    super(origin.getX(), origin.getY(), origin.getZ());  
  }
  
  public void setDirection(Vector3 direction) {
    this.direction = direction;
    length = 1;
    direction.normalize();
  }
  
  public void setTarget(Vector3 target) {
    direction = target.clone();
    direction.subtract(this);
    length = direction.getMagnitude();
    direction.normalize();
  }

  public Vector3 getDirection() {
    return direction;
  }
  
  public Vector3 getTarget() {
    Vector3 target = direction.clone();
    target.setMagnitude(length);
    target.add(this);
    return target;
  }
  
  public void calculateFirstTriangleIntersection(List<Triangle> triangles){
    double nextDist = Double.MAX_VALUE;
    
    for(Triangle t : triangles){
      if(intersectWith(t, nextDist)){
        nextDist = length;
      }
    }
  }
  
  public Intersection getTriangleIntersection(Triangle t){
    intersectWith(t, Double.MAX_VALUE);
    return intersection;
  }
    
  public boolean intersectWith(Triangle t, double maxLength){
    Vector3 n = t.getNormal(false);
    length = (n.getDotProduct(t.getA()) - n.getDotProduct(this)) / n.getDotProduct(getDirection());
    
    if(length > maxLength || length < 0)
      return false;
      
    if(intersection.testTriangleAndPoint(t, getTarget())){
      length -= Intersection.ERROR_CONSTANT;
      intersection.copy(getTarget());
      return true;
    }
    return false;
    
    
    //return intersection.testTriangleAndPoint(t, getTarget());
  }
  
  public Vector3 calculateFirstBoundaryIntersection(Boundary b){
    double[] intersection = new double[3];
    int[] quadrant = new int[3];
    double[] maxT = new double[3];
    double[] candidatePlane = new double[3];
    boolean inside = true;
    int i;
    int whichPlane;

    for(i = 0; i < 3; i++){
      if(get(i) < b.getMinorBounds().get(i)){
        quadrant[i] = QUAD_LEFT;
        candidatePlane[i] = b.getMinorBounds().get(i);
        inside = false;
      }
      else if (get(i) > b.getMajorBounds().get(i)){
        quadrant[i] = QUAD_RIGHT;
        candidatePlane[i] = b.getMajorBounds().get(i);
        inside = false;
      }
      else{
        quadrant[i] = QUAD_MIDDLE;
      }
    }

    if(inside){
      return this;
    }

    for(i = 0; i < 3; i++){
      if(quadrant[i] != QUAD_MIDDLE && getDirection().get(i) != 0)
        maxT[i] = (candidatePlane[i] - get(i)) / getDirection().get(i);
      else
        maxT[i] = -1;
    }

    whichPlane = 0;
    for(i = 1; i < 3; i++){
      if(maxT[whichPlane] < maxT[i])
        whichPlane = i;
    }

    if(maxT[whichPlane] < 0)
      return null;
    for(i = 0; i < 3; i++){
      if(whichPlane != i){
        intersection[i] = get(i) + maxT[whichPlane] * getDirection().get(i);
        if(intersection[i] < b.getMinorBounds().get(i) || 
           intersection[i] > b.getMajorBounds().get(i))
          return null;
      }
      else{
        intersection[i] = candidatePlane[i];
      }
    }
    return new Vector3(intersection);
  }
  
  @Override
  public String toString(){
    return "Origin: " + super.toString() + "\nTarget: " + getTarget() + "\nDirect: " + getDirection() + "\nLength: " + length;
  }

  public Intersection getIntersection() {
    return intersection;
  }  
  
  public double getLength(){
    return length;
  }
  
  public void setLength(double length){
    this.length = length;
  }
}
